
#include <assert.h>
#include <stdlib.h>
#include "utility.h"
#include "qr-common.h"
#include "qr-list.h"
#include "libfec-ecc.h"
#include "qr-frame.h"
#include "frame-filler.h"
#include "qr-mask.h"
#include "png-handler.h"
#include "qr-encoder.h"

QREncoder* qr_encoder_new()
{
	QREncoder* qr_encoder;

	qr_encoder = (QREncoder*)malloc(sizeof(QREncoder));
	if(NULL == qr_encoder)
	{
		RES_NULL("Invoke malloc error.");
	}
	qr_encoder->_ecc_adapter = (EccAdapter*)libfec_ecc_new();
	if(NULL == qr_encoder->_ecc_adapter)
	{
		free(qr_encoder);
		RES_NULL("Invoke libfec_ecc_new error.");
	}
	return qr_encoder;
}

void qr_encoder_destroy(QREncoder* qr_encoder)
{
	if(qr_encoder != NULL)
	{
		if(qr_encoder->_ecc_adapter != NULL)
		{
			ecc_adapter_destroy(qr_encoder->_ecc_adapter);
		}
		free(qr_encoder);
	}
}

int qr_encoder_simple_encode(QREncoder* qr_encoder, int version, ECLevel ec_level, QRMode mode, MaskPattern mask_pattern, unsigned char* source, int size, const char* filename)
{
	QRList* qr_list;
	QRElement* qr_element;
	unsigned char* data, * frame, * ptr, * masked;
	RSBlock* rs_block;
	FrameFiller* frame_filler;
	int entire_count, i, j;
	unsigned char code, bit;
	int remainder_count;

	if(NULL == qr_encoder || qr_check_version(version) < 0 || qr_check_ec_level(ec_level) < 0 || qr_check_mode(mode) < 0 || qr_check_mask_pattern(mask_pattern) < 0 || NULL == source || size <= 0 || NULL == filename)
	{
		RES_INVAL("Argument is invalid.");
	}
	if(NULL == qr_encoder->_ecc_adapter)
	{
		RES_INVAL("Argument is invalid.");
	}

	// QR encode.
	qr_list = qr_list_new(version, ec_level);
	if(NULL == qr_list)
	{
		RES_INVAL("Invoke qr_list_new error.");
	}
	qr_element = qr_element_new(mode, source, size);
	if(NULL == qr_element)
	{
		qr_list_destroy(qr_list);
		RES_INVAL("Invoke qr_element_new error.");
	}
	if(qr_list_append(qr_list, qr_element) < 0)
	{
		qr_element_destroy(qr_element);
		qr_list_destroy(qr_list);
		RES_INVAL("Invoke qr_list_append error.");
	}
	data = qr_list_encode(qr_list);
	qr_list_destroy(qr_list);
	if(NULL == data)
	{
		RES_INVAL("Invoke qr_list_encode error.");
	}
	// ECC encode.
	rs_block = rs_block_new(version, ec_level, data);
	if(NULL == rs_block)
	{
		free(data);
		RES_INVAL("Invoke rs_block_new error.");
	}
	if(ecc_adapter_encode(qr_encoder->_ecc_adapter, rs_block) < 0)
	{
		rs_block_destroy(rs_block);
		RES_INVAL("Invoke ecc_adapter_encode error.");
	}
	// Fill frame.
	frame = qr_new_frame(version);
	if(NULL == frame)
	{
		rs_block_destroy(rs_block);
		RES_INVAL("Invoke qr_new_frame error.");
	}
	frame_filler = frame_filler_new(frame, version);
	if(NULL == frame_filler)
	{
		free(frame);
		rs_block_destroy(rs_block);
		RES_INVAL("Invoke frame_filler_new error.");
	}
	entire_count = rs_block_entire_count(rs_block);
	if(entire_count <= 0)
	{
		frame_filler_destroy(frame_filler);
		free(frame);
		rs_block_destroy(rs_block);
		RES_INVAL("Invoke rs_block_entire_count error.");
	}
	for(i = 0; i < entire_count; ++i)
	{
		code = rs_block_get_code(rs_block, i);
		bit = 0x80;
		for(j = 0; j < 8; ++j)
		{
			ptr = frame_filler_next(frame_filler);
			assert(ptr != NULL);
			*ptr = ((0 == (code & bit)) ? 0x2 : 0x3);
			bit = bit >> 1;
		}
	}
	rs_block_destroy(rs_block);
	// Fill remainder bits.
	remainder_count = qr_remainder_count(version);
	if(remainder_count < 0)
	{
		frame_filler_destroy(frame_filler);
		free(frame);
		RES_INVAL("Invoke qr_remainder_count error.");
	}
	for(i = 0; i < remainder_count; ++i)
	{
	       ptr = frame_filler_next(frame_filler);
	       assert(ptr != NULL);
	       *ptr = 0x2;
	}
	frame_filler_destroy(frame_filler);
	// Masking.
	masked = make_mask(version, mask_pattern, frame);
	free(frame);
	if(NULL == masked)
	{
		RES_INVAL("Invoke make_mask error.");
	}
	// Put format information.
	if(qr_put_format_information(version, ec_level, mask_pattern, masked) < 0)
	{
		free(masked);
		RES_INVAL("Invoke qr_put_format_information error.");
	}
	// Write png.
	write_png_for_qrcode(version, masked, filename);
	free(masked);
	return 0;
}

